﻿using System;
using System.Collections.Generic;
using System.Text;
using Pixysoft.Collections;
using Pixysoft.Framework.Configurations;
using System.IO;
using Pixysoft.Framework.Verifications;
using System.Xml.Serialization;

namespace Pixysoft.Framework.Remoting.Controller
{
    class CacheController
    {
        private MostRecentlyUsedList<ICacheItem> mrulist = new MostRecentlyUsedList<ICacheItem>();

        private static volatile CacheController instance;

        private static object syncRoot = new Object();

        public static CacheController Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                        {
                            instance = new CacheController();
                        }
                    }
                }
                return instance;
            }
        }

        public bool IsOverdued(string methodDeclaration)
        {
            if (string.IsNullOrEmpty(methodDeclaration))
                return false;

            methodDeclaration = methodDeclaration.Trim().ToUpper();

            string key = Pixysoft.Security.MD5.GetMD5(methodDeclaration);

            foreach (ICacheItem cacherow in mrulist[key])
            {
                if (cacherow.IsOverdued())
                    return true;
            }

            return false;
        }

        public bool Contains(string methodDeclaration)
        {
            if (string.IsNullOrEmpty(methodDeclaration))
                return false;


            methodDeclaration = methodDeclaration.Trim().ToUpper();

            string key = Pixysoft.Security.MD5.GetMD5(methodDeclaration);

            return mrulist.Contains(key);
        }

        public void AddNullCache(string methodName, string methodDeclaration)
        {
            if (!Verification.Verifies(methodName, methodDeclaration).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(methodName, methodDeclaration);

            methodName = methodName.Trim().ToUpper();

            methodDeclaration = methodDeclaration.Trim().ToUpper();

            string key = Pixysoft.Security.MD5.GetMD5(methodDeclaration);

            mrulist.Remove(key);

            mrulist.Add(key, new ShallowCacheItem(StringHelper.default_cache_nulltimespan, key, methodName, null));
        }

        public void AddShallowCache(string methodName, string methodDeclaration, string methodResponse)
        {
            if (!Verification.Verifies(methodName, methodDeclaration).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(methodName, methodDeclaration);

            methodName = methodName.Trim().ToUpper();

            methodDeclaration = methodDeclaration.Trim().ToUpper();

            string key = Pixysoft.Security.MD5.GetMD5(methodDeclaration);

            mrulist.Remove(key);

            mrulist.Add(key, new ShallowCacheItem(StringHelper.default_cache_timespan, key, methodName, methodResponse));
        }

        public void AddDeepCache(string methodName, string methodDeclaration, string methodResponse)
        {
            AddDeepCache(StringHelper.default_cache_timespan, methodName, methodDeclaration, methodResponse);
        }

        public void AddDeepCache(int timespan, string methodName, string methodDeclaration, string methodResponse)
        {
            if (!Verification.Verifies(methodName, methodDeclaration).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(methodName, methodDeclaration);

            methodName = methodName.Trim().ToUpper();

            methodDeclaration = methodDeclaration.Trim().ToUpper();

            string key = Pixysoft.Security.MD5.GetMD5(methodDeclaration);

            mrulist.Remove(key);//2010-08-22 bug. 要先删除 后添加

            mrulist.Add(key, new DeepCacheItem(timespan, key, methodName, methodResponse));
        }

        public string GetMethodResponse(string methodDeclaration)
        {
            if (string.IsNullOrEmpty(methodDeclaration))
                return null;


            methodDeclaration = methodDeclaration.Trim().ToUpper();

            string key = Pixysoft.Security.MD5.GetMD5(methodDeclaration);

            List<ICacheItem> list = mrulist[key];

            if (list.Count > 0)
                return list[0].MethodResponse;

            return null;
        }

        /// <summary>
        /// 过滤空数据
        /// </summary>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public List<string> GetCacheMethodResponses(string methodName)
        {
            List<string> list = new List<string>();

            foreach (ICacheItem item in mrulist)
            {
                if (string.IsNullOrEmpty(item.MethodResponse))
                    continue;

                if (string.Equals(item.MethodName, methodName, StringComparison.OrdinalIgnoreCase))
                    list.Add(item.MethodResponse);
            }

            return list;
        }

        public bool RemoteMethodResponseAt(string methodName, int index)
        {
            int currentIndex = 0;

            ICacheItem removeItem = null;

            foreach (ICacheItem item in mrulist)
            {
                if (string.IsNullOrEmpty(item.MethodResponse))
                    continue;

                if (string.Equals(item.MethodName, methodName, StringComparison.OrdinalIgnoreCase)
                    && index == currentIndex++)
                {
                    removeItem = item;

                    break;
                }
            }

            if (removeItem == null)
                return false;

            mrulist.Remove(removeItem);

            return true;
        }

        public bool Remove(string methodDeclaration)
        {
            if (string.IsNullOrEmpty(methodDeclaration))
                return false;


            methodDeclaration = methodDeclaration.Trim().ToUpper();

            string key = Pixysoft.Security.MD5.GetMD5(methodDeclaration);

            mrulist.Remove(key);

            return true;
        }

        public void ClearAll()
        {
            mrulist.Clear();
        }

        public static void Load()
        {
            Load(true);
        }

        public static void Load(bool encrypt)
        {
            string path = StringFilter.RegularPath(AppDomain.CurrentDomain.BaseDirectory) +
                            StringHelper.default_setting_filename;

            if (!File.Exists(path))
                return;

            string content = null;

            try
            {
                content = File.ReadAllText(path);

                if (encrypt)
                    content = Pixysoft.Security.DES.Decrypt(content, StringHelper.global_framework_id);

                content = Pixysoft.IO.Compressor.Instance.DeCompress(content);
            }
            catch (Exception ex)
            {
                LoggerHelper.Error(ex.ToString());

                Instance.CacheItems = new List<DeepCacheItem>();

                return;
            }

            List<DeepCacheItem> ds = null;

            try
            {
                ds = XmlManager.Deserialize<List<DeepCacheItem>>(content);

                Instance.CacheItems = ds;
            }
            catch (Exception ex)
            {
                LoggerHelper.Fatal(ex);

                Instance.CacheItems = new List<DeepCacheItem>();

            }
        }

        public static void Save()
        {
            Save(true);
        }

        public static void Save(bool encrypt)
        {
            string content = XmlManager.Serialize(Instance.CacheItems).Serialize(true);

            content = Pixysoft.IO.Compressor.Instance.Compress(content);

            if (encrypt)
                content = Pixysoft.Security.DES.Encrypt(content, StringHelper.global_framework_id);

            try
            {
                File.WriteAllText(
                StringFilter.RegularPath(AppDomain.CurrentDomain.BaseDirectory) + StringHelper.default_setting_filename, content);
            }
            catch (Exception ex)
            {
                LoggerHelper.Fatal(ex);
            }
        }

        private List<DeepCacheItem> CacheItems
        {
            get
            {
                List<DeepCacheItem> rlist = new List<DeepCacheItem>();
                foreach (ICacheItem item in mrulist)
                {
                    if (item is DeepCacheItem)
                        rlist.Add(item as DeepCacheItem);
                }
                return rlist;
            }

            set
            {
                ClearAll();

                foreach (DeepCacheItem item in value)
                {
                    mrulist.Add(item.CacheKey, item);
                }
            }
        }
    }

    class DeepCacheItem : ShallowCacheItem
    {
        public DeepCacheItem()
        {
        }

        public DeepCacheItem(int timespan, string cacheKey, string methodName, string methodResponse)
            : base(timespan, cacheKey, methodName, methodResponse)
        {
        }
    }

    class ShallowCacheItem : ICacheItem
    {

        string cacheKey;
        DateTime createDate;
        DateTime endDate;
        int hitRate;

        string methodName;
        string methodResponse;

        public ShallowCacheItem() { }

        public ShallowCacheItem(int timespan, string cacheKey, string methodName, string methodResponse)
        {
            this.cacheKey = cacheKey;
            this.methodName = methodName;
            this.methodResponse = methodResponse;


            this.CreateDate = Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime();

            if (timespan < 0)
                this.EndDate = DateTime.MaxValue;
            else
                this.EndDate = this.CreateDate.AddMilliseconds(timespan);

            this.hitRate = 0;
        }

        [XmlAttribute("cachekey")]
        public string CacheKey
        {
            get { return cacheKey; }
            set { cacheKey = value; }
        }

        [XmlAttribute("createdate")]
        public DateTime CreateDate
        {
            get { return createDate; }
            set { createDate = value; }
        }

        [XmlAttribute("enddate")]
        public DateTime EndDate
        {
            get { return endDate; }
            set { endDate = value; }
        }

        [XmlAttribute("hitrate")]
        public int HitRate
        {
            get { return hitRate; }
            set { hitRate = value; }
        }

        public void Hit()
        {
            hitRate++;
        }

        public string MethodName
        {
            get { return methodName; }
            set { methodName = value; }
        }

        public string MethodResponse
        {
            get { return methodResponse; }
            set { methodResponse = value; }
        }

        public bool IsOverdued()
        {
            return this.EndDate < Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTime();
        }
    }

    interface ICacheItem
    {
        string CacheKey { get;}

        DateTime CreateDate { get;}

        DateTime EndDate { get;}

        int HitRate { get;}

        void Hit();

        string MethodName { get;}

        string MethodResponse { get;set;}

        bool IsOverdued();

    }
}

